/*
 * @[H]:  Copyright (c) 2021 Phytium Information Technology, Inc. 
 * 
 *  SPDX-License-Identifier: Apache-2.0. 
 * 
 * @Date: 2021-08-31 08:10:49
 * @LastEditTime: 2021-09-07 09:07:26
 * @Description:  Description of file
 * @Modify History: 
 * * * Ver   Who        Date         Changes
 * * ----- ------     --------    --------------------------------------
 */

#include "sys_init.h"
#include "sdkconfig.h"
#include "cache.h"
#include "interrupt.h"
#include "ft_types.h"
#include "parameters.h"
#include "_cpu.h"

#include "common.h"
#define METAL_INTERNAL
#include <metal/errno.h>
#include <string.h>
#include <metal/io.h>
#include <metal/alloc.h>
#include <metal/device.h>

#include "ft_debug.h"
#define LIBMETAL_SYS_DEBUG_TAG "LIBMETAL_SYS"
#define LIBMETAL_SYS_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(LIBMETAL_SYS_DEBUG_TAG, format, ##__VA_ARGS__)
#define LIBMETAL_SYS_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(LIBMETAL_SYS_DEBUG_TAG, format, ##__VA_ARGS__)
#define LIBMETAL_SYS_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(LIBMETAL_SYS_DEBUG_TAG, format, ##__VA_ARGS__)


#define SHM_BASE_ADDR CONFIG_SHM_BASE_ADDR

/* Default generic I/O region page shift */
/* Each I/O region can contain multiple pages.
 * In baremetal system, the memory mapping is flat, there is no
 * virtual memory.
 * We can assume there is only one page in the whole baremetal system.
 */
#define DEFAULT_PAGE_SHIFT (-1UL)
#define DEFAULT_PAGE_MASK  (-1UL)

const metal_phys_addr_t metal_phys[] = {
	SHM_BASE_ADDR, /**< shared memory base address */
};

static struct metal_device metal_dev_table[] = {
	{
		/* Shared memory management device */
		.name = SHM_DEV_NAME,
		.bus = NULL,
		.num_regions = 1,
		.regions = {
			{
				.virt = (void *)SHM_BASE_ADDR,
				.physmap = &metal_phys[1],
				.size = 0x1000000,
				.page_shift = DEFAULT_PAGE_SHIFT,
				.page_mask = DEFAULT_PAGE_MASK,
				.ops = {NULL},
			}
		},
		.node = {NULL},
		.irq_num = 0,
		.irq_info = NULL,
	}
};


/**
 * Extern global variables
 */

struct metal_device *shm_dev = NULL;

/**
 * @brief EnableCaches() - Enable caches
 */

void EnableCaches()
{
    FCacheICacheEnable() ;
    FCacheDCacheEnable() ;
}


/**
 * @brief DisableCaches() - Disable caches
 */
void  DisableCaches()
{
    FCacheDCacheDisable() ;
    FCacheICacheDisable() ;
}




/**
 * @brief platform_register_metal_device() - Statically Register libmetal
 *        devices.
 *        This function registers the IPI, shared memory and
 *        TTC devices to the libmetal generic bus.
 *        Libmetal uses bus structure to group the devices. Before you can
 *        access the device with libmetal device operation, you will need to
 *        register the device to a libmetal supported bus.
 *        For non-Linux system, libmetal only supports "generic" bus, which is
 *        used to manage the memory mapped devices.
 *
 * @return 0 - succeeded, non-zero for failures.
 */
s32 PlatformRegisterMetalDevice(void)
{
    u32 i ;
    s32 ret ;
    struct metal_device *dev;
    for (size_t i = 0; i <sizeof(metal_dev_table)/sizeof(struct metal_device);
         i++)
    {
        dev = &metal_dev_table[i];
        LIBMETAL_SYS_DEBUG_I("registering: %d, name=%s\n", i, dev->name) ;
        ret = metal_register_generic_device(dev);
        if (ret)
			return ret;
    }
    return 0;

}

/**
 * @brief open_metal_devices() - Open registered libmetal devices.
 *        This function opens all the registered libmetal devices.
 *
 * @return 0 - succeeded, non-zero for failures.
 */
s32 OpenMetalDevice(void)
{
    int ret;

    /* Open shared memory device */
	ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev);
	if (ret) {
		LIBMETAL_SYS_DEBUG_E("Failed to open device %s.\n", SHM_DEV_NAME);
		goto out;
	}

out:
    return ret ;
}


/**
 * @brief CloseMetalDevice() - close libmetal devices
 *        This function closes all the libmetal devices which have
 *        been opened.
 *
 */
void CloseMetalDevice(void)
{
    /* Close shared memory device */
	if (shm_dev)
		metal_device_close(shm_dev);
}


s32 SysInit()
{
    struct metal_init_params metal_param = METAL_INIT_DEFAULTS;
	s32 ret;

    /* Initialize libmetal environment */
	metal_init(&metal_param);
    
    /* Register libmetal devices */
    ret = PlatformRegisterMetalDevice();
    if (ret) {
		LIBMETAL_SYS_DEBUG_E("%s: failed to register devices: %d\n", __func__, ret);
		return ret;
	}

    /* Open libmetal devices which have been registered */
	ret = OpenMetalDevice();
	if (ret) {
		LIBMETAL_SYS_DEBUG_E("%s: failed to open devices: %d\n", __func__, ret);
		return ret;
	}
	return 0;
}


/**
 * @brief SysCleanup() - system cleanup
 *        This function finish the libmetal environment
 *        and disable caches.
 *
 * @return 0 - succeeded, non-zero for failures.
 */
void SysCleanup()
{
	/* Close libmetal devices which have been opened */
	CloseMetalDevice();
	/* Finish libmetal environment */
	metal_finish();
	DisableCaches();
}


